#include "ulib.h"

struct ramfs_node_t
{
	 uint8_t type;
	 union
	 {
		  /* the start address in memory */
		  struct
		  {
			   void   *data;
			   faddr_t size;
		  };

		  /* the mapping entry */
		  struct
		  {
			   int mapping_entry;
		  };
	 };
};

struct ramfs_data_t
{
	 int node_id;
	 int mapping_id;
};

/* ramfs node data header in image */
struct iramfs_node_t
{
	 fchar_t name[RAMFS_NAME_LENGTH + 1];
	 uint8_t type;
	 /* directory level, for parsing */
	 uint32_t level;
	 faddr_t size;
};

static int
ramfs_map(struct fs_node_t *f, faddr_t start, int size, void *addr)
{
	 struct ramfs_t *fs = f->drv->data;
	 struct ramfs_data_t *data = (struct ramfs_node_t *)f->data;
	 struct ramfs_node_t *node = fs->node + data->node_id;
	 
	 if (node->type == FILE_NORMAL)
	 {
		  if (start >= node->size) return 0;
		  if (start + size >= node->size) size = node->size - start;
		  memmove(addr, (char *)node->data + start, size);
		  return size;
	 }
	 else if (node->type == FILE_DIR)
		  return -1;
}

static int
ramfs_open(struct fs_node_t *d, struct fs_node_t *f)
{
	 struct ramfs_t *fs = f->drv->data;
	 struct ramfs_data_t *data = (struct ramfs_node_t *)f->data;
	 struct ramfs_node_t *node = fs->node + data->node_id;

	 if (node->type != FILE_DIR)
		  return -1;
	 else if (data->mapping_id == -1)
		  return -1;

	 f->drv = &ramfs_drv;
	 int node_id = fs->maps[data->mapping_id].node_id;
	 if (fs->nodes[node_id].type == FILE_NORMAL)
	 {
		  f->type = FILE_NORMAL;
		  f->data = ramfs_node_alloc(node_id, -1);
	 }
	 else
	 {
		  f->type = FILE_DIR;
		  f->data = ramfs_node_alloc(node_id, fs->nodes[node_id].mapping_entry);
	 }

	 return 0;
}

static int
ramfs_stat(struct fs_node_t *f, struct fs_stat_t *attr)
{
	 struct ramfs_node_t *data = (struct ramfs_node_t *)f->data;
	 attr->type = data->type;
	 attr->size = data->size;
	 return 0;
}

static int
ramfs_close(struct fs_node_t *f)
{
	 /* NOTHING */
}

static fs_driver_t ramfs_drv;

int
ramfs_init(struct fs_node_t *f)
{
	 ramfs_drv.map    = ramfs_map;
	 ramfs_drv.update = NULL;
	 ramfs_drv.open   = ramfs_open;
	 ramfs_drv.stat   = ramfs_stat;
	 ramfs_drv.move   = NULL;
	 ramfs_drv.close  = ramfs_close;

	 uint32_t fcount = *(uint32_t *)img;
	 img += sizeof(uint32_t);

	 struct ramfs_node_t *rfs_nodes = bmalloc(sizeof(struct ramfs_node_t) * fcount + 1);
	 int rfs_cur = 1;
	 
	 struct ramfs_node_t *rfs_stack[16];
	 rfs_stack[0] = rfs_nodes;
	 
	 int i;
	 for (i = 0; i != fcount; ++ i)
	 {
		  struct iramfs_node_t *inode = (struct iramfs_node_t *)img;
		  img = (const char *)(inode + 1);

		  struct ramfs_node_t *cur = rfs_nodes + (rfs_cur ++);
		  memmove(&cur->name[0], &inode->name[0], 16);
		  cur->type = inode->type;

		  struct ramfs_node_t *node = rfs_stack[inode->level - 1];
		  node->children[node->children_count ++] =
			   cur;
		  rfs_stack[inode->level] = cur;

		  if (cur->type == FILE_DIR)
		  {
			   cur->children_count = 0;
		  }
		  else
		  {
			   cur->data = (void *)img;
			   img += (cur->size = inode->size);
		  }
	 }
	 
	 f->type = FILE_DIR;
	 f->drv  = &ramfs_drv;
	 mutex_init(&f->lock);
	 f->data = rfs_nodes;

}
